package objects;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Random;

import common.RealmConf;
import common.RealmServer;
import common.SQLMgr;
import common.PacketFormatter;

public class RealmThread implements Runnable
{
	private Socket _s;
	private Thread _t;
	private BufferedReader _in;
	private PrintWriter _out;
	private boolean _running = false;
	private String _hashKey = "", _accountName = "";
	private byte _nextPacket = 0;
	private String _ip;
	private Account _account;
    public static byte gsIDSelect;
	
	public RealmThread(Socket s)
	{
		_s = s;
		try
		{
			_in = new BufferedReader(new InputStreamReader(_s.getInputStream()));
			_out = new PrintWriter(_s.getOutputStream());
		}catch(Exception e){return;};
		_ip = _s.getInetAddress().getHostAddress();
		
		RealmServer.writeLine("New Connection from "+_ip);
                
		_t = new Thread(this);
		_t.setDaemon(true);
		_t.start();
	}

	public void run()
	{
		_running = true;
		try
    	{
			String packet = "";
			char charCur[] = new char[1];
			
			//Send policy file if needed
			if(RealmConf._sendPolicy)
				sendPacket(PacketFormatter.getPolicyFilePacket());
			
			//Generate a new randomized HashKey
			String alphabet = "abcdefghijklmnopqrstuvwxyz";
	        Random rand = new Random();
	        for (int i=0; i<32; i++)_hashKey += alphabet.charAt(rand.nextInt(alphabet.length()));
	       
	        //Send HelloConnection Packet
	        sendPacket(PacketFormatter.getHelloConnectionPacket(_hashKey));
	        
	        //Start to read incoming Packets
	    	while(_in.read(charCur, 0, 1)!=-1 && RealmServer.isRunning() && _running)
	    	{
	    		if (charCur[0] != '\u0000' && charCur[0] != '\n' && charCur[0] != '\r')
		    	{
	    			packet += charCur[0];
		    	}else if(packet != "")
		    	{
		    		if(RealmConf._debug)RealmServer.writeLine("Recv << "+packet);
		    		parsePacket(packet);
		    		packet = "";
		    	}
	    	}
    	}catch(IOException e)
    	{
    		//todo
    	}
    	finally
    	{
    		_out.close();
    		try
    		{
    			_in.close();
    		}catch(Exception e){}
			try
    		{
    			_s.close();
    		}catch(Exception e){}
			RealmServer.writeLine("Connection from "+_ip+" closed");
			_account.setConnected(false);
    	}
	}
	
	private void parsePacket(String packet)
	{
		switch(_nextPacket)
		{
		case 0://Version
			//If wrong version
			if(!packet.equalsIgnoreCase("1.29.1"))
			{
				sendPacket(PacketFormatter.getWrongVersionPacket());
				_running = false;//Stop Packet reading
				
				return;
			}
			_nextPacket++;
			
			break;
			
		case 1://Account Name
			_accountName = packet;
			_nextPacket++;
			
		break;
		
		case 2://Password
			//Should never be true
			if(!packet.substring(0, 2).equalsIgnoreCase("#1"))
			{
				_running = false;
				RealmServer.writeLine("Receive unknown packet from "+_ip+": client kicked");
				return;
			}
			verifAccount(packet.substring(2));
			_nextPacket++;
			
		break;
		
		}
		
		if(_nextPacket<3)return;
		
		//Hack ?
		if(packet.charAt(0) != 'A' && packet.charAt(0) != '#')
		{
			_running = false;
			RealmServer.writeLine("Receive unknown packet from "+_ip+": client kicked");
			return;
		}
		
		switch(packet.charAt(1))
		{
		case 'f'://Waiting Queue
			//TODO: Queue System
			sendPacket(PacketFormatter.getAccountWaitingQueuePacket
					((short)1, (short)1, (short)1, _account.isSubscriber(), (byte)1));
			break;
			
		case 'x'://Ask Character List
			if(_account == null)
			{
				_running = false;
				RealmServer.writeLine("Receive unexpected packet from "+_ip+": client kicked");
				return;
			}
                        sendPacket(PacketFormatter.getAccountServerListPacket(_account.getGuid(),_account.getSubscribeTimeEnd()));
                        break;
		
		case 'X'://Select GameServer
			if(_account == null)
			{
				_running = false;
				RealmServer.writeLine("Receive unexpected packet from "+_ip+": client kicked");
				return;
			}
			byte gsID = -1;
			try
			{
				gsID = Byte.parseByte(packet.substring(2));
			}catch(Exception e)
			{
				_running = false;
				RealmServer.writeLine("Receive bad formatted packet from "+_ip+": client kicked");
				return;
			}
			GameServer GS = RealmServer.getGameServers().get(gsID);
			//If non registered game server
			if(GS == null)
			{
				_running = false;
				RealmServer.writeLine("Receive unknown game server ID from "+_ip+": client kicked");
				return;
			}
			
			//Send a packet to put the account in Waiting list on the GameServer
			GS.getInterServThread().registerNewWaitingAccount(_account);
			
			//If Loopback is activated, verify if client is LocalClient
			String ipToUse = GS.getAddress();
			if(RealmConf._localhostLoopback && ipToUse.equals(_ip))ipToUse = "127.0.0.1";
			
                        gsIDSelect = gsID;
                        
			//Send GameServer Address & Port to the client
			if(GS.isUsingIpAddress())//Active Crypter
				sendPacket(PacketFormatter.getCryptedGameServerAddressPacket(ipToUse,GS.getPort(), _account.getGuid()));
			else
				sendPacket(PacketFormatter.getClearGameServerAddressPacket(ipToUse,GS.getPort(), _account.getGuid()));
			break;
		}
	}
	
	private void verifAccount(String pass)
	{
		//If error
		if(_hashKey.equals("") || _accountName.equals("") || pass.equals("") || _nextPacket != 2)
		{
			sendPacket(PacketFormatter.getAccountConnexionDefaultErrorPacket());
			_running = false;
			return;
		}
		//If invalid account
		long guid = SQLMgr.verifAccount(_accountName,pass,_hashKey,_ip);
		//If error
		if(guid  < 0 )
		{
			String packet = "";
			switch((int)guid)
			{
			case -1://SQLMgr no initialized
				RealmServer.writeLine("Error: SQLMgr was not initialized before call");
				sendPacket(PacketFormatter.getAccountConnexionDefaultErrorPacket());
				break;
			case -2://Get Result Failed
				RealmServer.writeLine("Error: The Account Guid cannot be fetched (account = "+_account+")");
				sendPacket(PacketFormatter.getAccountConnexionDefaultErrorPacket());
				break;
			case -3://Account Banned
				sendPacket(PacketFormatter.getAccountBannedPacket());
				break;
			case -4://No Account Matched
				sendPacket(PacketFormatter.getInvalidAccountPacket());
				break;
			}
			if(!packet.equals(""))
				sendPacket(packet);
			
			_running = false;//Stop packet reading
			return;
		}
		_account = SQLMgr.getAccountDataByID(guid);
		if(_account == null)
		{
			RealmServer.writeLine("Error: Account Datas cannot be fetched (guid = "+guid+")");
			sendPacket(PacketFormatter.getAccountConnexionDefaultErrorPacket());
			_running = false;
			return;
		}
		
		_account.setConnected(true);
		
		//Send Packets
		sendPacket(PacketFormatter.getAccountDofusPseudoPacket(_account.getPseudo()));
		sendPacket(PacketFormatter.getAccountCommunityPacket(0));
		sendPacket(PacketFormatter.getAccountHostListPacket(_account.isSubscriber()));
		sendPacket(PacketFormatter.getAccountLoginOkPacket(_account.getGmLevel()>0));
		sendPacket(PacketFormatter.getAccountQuestionPacket(_account.getQuestion()));
	}
	
    public void sendPacket(String packet)
    {
    	if(!_s.isConnected() || _s.isClosed())return;
    	
    	try
    	{
    		_out.write(packet + (char)0x00);
    		_out.flush();
    		if(RealmConf._debug)RealmServer.writeLine("Send >> "+packet);
    	}catch(Exception e)
    	{
    		_running = false;
    	};
    }

	public Account getAccount()
	{
		return _account;
	}

	public PrintWriter getOut() {
		return _out;
	}

}
